package com.example.user.service.permission;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.ObjUtil;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.example.framework.common.enums.CommonStatusEnum;
import com.example.framework.common.exception.ServiceException;
import com.example.user.controller.permission.vo.role.RoleSaveReqVO;
import com.example.user.dal.dataobject.permission.RoleDO;
import com.example.user.dal.mysql.permission.RoleMapper;
import com.example.user.dal.redis.RedisKeyConstants;
import com.google.common.annotations.VisibleForTesting;
import jakarta.annotation.Resource;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import static com.example.user.constants.ErrorCodeConstants.ROLE_NAME_DUPLICATE;
import static com.example.user.constants.ErrorCodeConstants.ROLE_NOT_EXISTS;

@Service
public class RoleServiceImpl extends ServiceImpl<RoleMapper, RoleDO> implements RoleService {

    @Resource
    private RoleMapper roleMapper;

    @Resource
    @Lazy // 延迟，避免循环依赖报错
    private PermissionService permissionService;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long createRole(RoleSaveReqVO createReqVO, Integer type) {
        // 1. 校验角色
        validateRoleDuplicate(createReqVO.getName(), null);

        // 2. 插入到数据库
        RoleDO role = RoleDO.builder()
                .name(createReqVO.getName())
                .status(ObjUtil.defaultIfNull(createReqVO.getStatus(), CommonStatusEnum.ENABLE.getStatus()))
                .build();
        roleMapper.insert(role);
        return role.getId();
    }

    @Override
    @CacheEvict(value = RedisKeyConstants.ROLE, key = "#updateReqVO.id")
    public void updateRole(RoleSaveReqVO updateReqVO) {
        // 1.1 校验是否可以更新
        validateRoleForUpdate(updateReqVO.getId());
        // 1.2 校验角色的唯一字段是否重复
        validateRoleDuplicate(updateReqVO.getName(), updateReqVO.getId());

        // 2. 更新到数据库
        RoleDO updateObj = BeanUtil.toBean(updateReqVO, RoleDO.class);
        roleMapper.updateById(updateObj);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    @CacheEvict(value = RedisKeyConstants.ROLE, key = "#id")
    public void deleteRole(Long id) {
        // 1. 校验是否可以更新
        validateRoleForUpdate(id);

        // 2.1 标记删除
        roleMapper.deleteById(id);
        // 2.2 删除相关数据
        permissionService.processRoleDeleted(id);
    }

    @Override
    public RoleDO getRole(Long id) {
        return roleMapper.selectById(id);
    }

    /**
     * 校验角色的唯一字段是否重复
     * <p>
     * 1. 是否存在相同名字的角色
     * 2. 是否存在相同编码的角色
     *
     * @param name 角色名字
     * @param id   角色编号
     */
    @VisibleForTesting
    private void validateRoleDuplicate(String name, Long id) {
        // 1. 该 name 名字被其它角色所使用
        RoleDO role = roleMapper.selectOne(Wrappers.<RoleDO>lambdaQuery()
                .eq(RoleDO::getName, name));
        if (role != null && !role.getId().equals(id)) {
            throw ServiceException.of(ROLE_NAME_DUPLICATE, name);
        }
    }

    /**
     * 校验角色是否可以被更新
     *
     * @param id 角色编号
     */
    @VisibleForTesting
    private void validateRoleForUpdate(Long id) {
        RoleDO role = roleMapper.selectById(id);
        if (role == null) {
            throw ServiceException.of(ROLE_NOT_EXISTS);
        }
    }
}
